package com.csx.governance.traffic.route.swimlane;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import com.csx.governance.traffic.condition.Component;
import com.csx.governance.traffic.condition.ComponentFactory;
import com.csx.governance.traffic.condition.ComponentsCompiler;
import com.csx.governance.traffic.condition.ParsedExpression;
import com.csx.governance.traffic.core.TrafficGovernanceRuleLocator;
import com.csx.governance.traffic.route.model.ConditionExpression;
import com.csx.governance.traffic.route.model.swimlane.SwimLaneDefinition;
import com.csx.governance.traffic.route.model.swimlane.SwimLaneRule;
import com.csx.governance.traffic.route.model.swimlane.SwimLaneServiceRule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 泳道(全链路)治理规则定位器
 * @author cuisongxu
 * @date 2023/2/23 15:47
 */
public class SwimLaneGovernanceRuleLocator implements TrafficGovernanceRuleLocator<SwimLaneServiceRule> {

    private static final Logger logger = LoggerFactory.getLogger(SwimLaneGovernanceRuleLocator.class);

    private final SwimLaneRulesConfiguration swimLaneRulesConfiguration;

    private final SwimLaneDefinitionsConfiguration swimLaneDefinitionsConfiguration;

    private final ComponentFactory componentFactory;

    /**
     * swimLaneId -> serviceName -> SwimLaneSubDefinition
     */
    private volatile Map<String, Map<String, SwimLaneDefinition.SwimLaneSubDefinition>> swimLaneIdToServiceNameToSwimLaneSubDefinitionMap;

    /**
     * swimLaneId -> baseSwimLaneId
     */
    private volatile Map<String, String> swimLaneIdToBaseSwimIdLaneIdMap;

    public SwimLaneGovernanceRuleLocator(SwimLaneRulesConfiguration swimLaneRulesConfiguration,
                                         SwimLaneDefinitionsConfiguration swimLaneDefinitionsConfiguration,
                                         ComponentFactory componentFactory) {
        this.swimLaneRulesConfiguration = swimLaneRulesConfiguration;
        this.swimLaneDefinitionsConfiguration = swimLaneDefinitionsConfiguration;
        this.componentFactory = componentFactory;
    }

    public SwimLaneDefinition.SwimLaneSubDefinition getSwimLaneSubDefinition(String swimLaneId, String serviceName) {

        Map<String, SwimLaneDefinition.SwimLaneSubDefinition> serviceNameToSwimSubDefinitionMap = swimLaneIdToServiceNameToSwimLaneSubDefinitionMap.get(swimLaneId);
        if(CollectionUtil.isEmpty(serviceNameToSwimSubDefinitionMap)) {
            return null;
        }
        return serviceNameToSwimSubDefinitionMap.get(serviceName);
    }

    public String getBaseSwimLaneIdBySwimLaneId(String swimLaneId) {
        return swimLaneIdToBaseSwimIdLaneIdMap.get(swimLaneId);
    }

    public String getGlobalBaseSwimLaneId() {
        List<SwimLaneDefinition> collect = swimLaneDefinitionsConfiguration.stream().filter(swimLaneDefinition -> swimLaneDefinition.getBaseSwimLaneId() == null).collect(Collectors.toList());
        if ((CollectionUtil.isNotEmpty(collect))) {
            return collect.get(0).getSwimLaneId();
        }
        return null;
    }

    @Override
    public void refresh() {
        logger.info("swimLane Governance Rule begin refresh");
        swimLaneIdToServiceNameToSwimLaneSubDefinitionMap = swimLaneDefinitionsConfiguration.stream()
                .collect(Collectors.toMap(SwimLaneDefinition::getSwimLaneId, Function.identity()))
                .entrySet()
                .stream()
                .collect(Collectors.toMap(Map.Entry::getKey, (entry) -> {
                    String baseSwimLaneId = entry.getValue().getBaseSwimLaneId();
                    return entry.getValue().getSwimLaneSubDefinitions()
                            .stream()
                            .peek((rule) -> rule.setBaseSwimLaneId(baseSwimLaneId)).collect(Collectors.toMap(SwimLaneDefinition.SwimLaneSubDefinition::getServiceName, Function.identity()));
                }));

        // TODO 空值问题
        swimLaneIdToBaseSwimIdLaneIdMap = swimLaneDefinitionsConfiguration.stream()
                .collect(Collectors.toMap(SwimLaneDefinition::getSwimLaneId, SwimLaneDefinition::getBaseSwimLaneId));

        swimLaneRulesConfiguration.forEach(swimLaneServiceRule -> {
            List<SwimLaneRule> rules = swimLaneServiceRule.getRules();
            rules.forEach(rule -> {
                ConditionExpression conditionExpression = rule.getConditionExpression();
                List<Component> components = conditionExpression.stream().map(expression -> componentFactory.build(expression.getType(), expression.getParams()))
                        .collect(Collectors.toList());
                ComponentsCompiler componentsCompiler = new ComponentsCompiler(components);
                ParsedExpression parsedExpression = componentsCompiler.parse();
                // 缓存编译后的表达式
                conditionExpression.setParsedExpression(parsedExpression);
            });
        });
        logger.info("swimLane Governance Rule finish refresh");
    }

    @Override
    public SwimLaneServiceRule locateRule(String serviceName) {
        Assert.notNull(serviceName, "serviceName must not be null");
        List<SwimLaneServiceRule> targetSwimLaneServiceRules = swimLaneRulesConfiguration.stream().filter(swimLaneServiceRule -> serviceName.equals(swimLaneServiceRule.getServiceName())).collect(Collectors.toList());
        if(CollectionUtil.isNotEmpty(targetSwimLaneServiceRules)) {
            return targetSwimLaneServiceRules.get(0);
        }
        return null;
    }
}
